package com.bc.mcode.core;

import com.bc.mcode.collect.Lists;
import com.bc.mcode.collect.Maps;
import com.bc.mcode.model.ClockVO;
import com.bc.mcode.service.ClockVOService;
import com.bc.mcode.util.SpringUtil;
import com.bc.mcode.util.StringX;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 响应队列
 * 这里发送响应失败的队列
 */
@Service
public class ClockResponse {

    protected Logger log = LogManager.getLogger(getClass().getName());

    //单例模式对象
    public static ClockResponse manager = new ClockResponse();

    //失败队列列表
    public static List<ClockVO> list = Collections.synchronizedList(Lists.newArrayList());

    public static  Map<Integer, Integer> callTimeMap = Maps.newHashMap();

    private ClockResponse() {}

    /**
     * 单例模式，使用一个响应对象
     * @return
     */
    public static ClockResponse getInstance(){
        return manager;
    }


    private void updateStatus(ClockVO clockVO, int state){
        ClockVO cl  = new ClockVO();
        cl.setClockState(state);
        cl.setClockId(clockVO.getClockId());
        cl.setClockExt1(StringX.getCurrentDate());
        Integer callTime = clockVO.getCallTime();
        if(null == callTime){
            callTime = 0;
        }
        int newCallTime = callTime+1;
        clockVO.setCallTime(newCallTime);
        cl.setCallTime(newCallTime);
        Object clockVOService = SpringUtil.getBean("ClockVOService");
        //Map<String, Object> map = ((ClockVOService) clockVOService).updateByPrimaryKeySelective(cl);
        clockVO.setClockState(state);
        //失败3次删除
        if(newCallTime >=3){
            deleClockVO(clockVO.getClockId());
        }
    }

    /**
     * 发送响应
     * @param clockVO
     */
    public boolean responseMain(ClockVO clockVO)  {
        Integer clockId = clockVO.getClockId();
        int callTime = 0;

        // 如果超过3次 不调用了 这里是对这个 clockId的双重校验 http响应超时的情况下 这里会多次尝试，因为只有响应后才更新 一个colock的信息，
        // 响应超时情况下 会调用多次 所以 1.失败轮训定时器时间设置为30秒 超过响应时间  2.增加调用次数的校验  3.响应后将调用次数信息加入数据库 超过3次后不在尝试
        if(!callTimeMap.containsKey(clockId)){
            callTimeMap.put(clockId,1);
        }else{
            callTimeMap.put(clockId, callTimeMap.get(clockId)+1);
        }
        if(callTime >3){
            log.error("尝试超过3次,不在调用,clock_id:"+ clockVO.getClockId());
            return false;
        }

        String url = clockVO.getClockUrl();
        log.info("do task:" + clockVO.toJsonString());
        HttpPost httpPost = new HttpPost(url);
        List<NameValuePair> nvps = new ArrayList<>();
        nvps.add(new BasicNameValuePair("mark", clockVO.getClockMark()));
        nvps.add(new BasicNameValuePair("data", clockVO.getClockData()));
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nvps));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        CloseableHttpAsyncClient httpclient = HttpAsyncClients.createDefault();
        httpclient.start();
        httpclient.execute(httpPost, new FutureCallback<HttpResponse>() {
            @Override
            public void completed(final HttpResponse response) {
                try {
                    String content = EntityUtils.toString(response.getEntity(), "UTF-8");
                    int status = 3;
                    if(content.contains("true")){
                        status = 2;
                    }
                    updateStatus(clockVO,status);
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    try {
                        httpclient.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            @Override
            public void failed(final Exception ex) {
                try {
                    updateStatus(clockVO,3);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        httpclient.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            @Override
            public void cancelled() {
                try {
                    updateStatus(clockVO,3);
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    try {
                        httpclient.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        return false;
    }

    /**
     * 第一次调用时的响应方法
     * @param clockVO
     */
    public void responseFrist(ClockVO clockVO) {
        //先将状态是1的加入队列，但是暂时不处理，等异步回调回来时候，状态就会被改成2,3
        addClockVO(clockVO);
        responseMain(clockVO);
    }

    //添加到失败队列
    public void  addClockVO(ClockVO clockVO){
        this.list.add(clockVO);
    }

    /**
     *  继续发送队列 主方法
     */
    public void taskMain(){
        List<ClockVO> removeList = Lists.newArrayList();
        for (int i=0; i< list.size();i++){
            ClockVO clockVO = list.get(i);
            int status = clockVO.getClockState();
            //3发送失败 1未发送
            if(status == 3 || status == 1){
                //尝试超过3次 不在发送
                Integer callTime = clockVO.getCallTime();
                if(null == callTime){
                    callTime = 0;
                }
                if(callTime <3){
                    responseMain(clockVO);
                }
                //2发送成功
            }else if (status == 2 ){
                removeList.add(clockVO);
            }
        }
        list.removeAll(removeList);
    }

    /**
     *  继续发送队列 30 秒重试一次
     */
    public void start(){
        log.info("继续发送队列 ClockResponse start V2...");
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(30);
        pool.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                taskMain();
            }
        }, 1000, 60000, TimeUnit.MILLISECONDS);
    }

    /**
     * 获取失败队列数据长度
     * @return
     */
    public int getFailDataNum(){
        int count = 0;
        for (int i=0;i<list.size();i++){
            if(list.get(i).getClockState()==3){
                count ++;
            }
        }
        return count;
    }

    public int getTotalDataNum(){
        return list.size();
    }

    /**
     * 获取失败队列数据
     * @return
     */
    public List<ClockVO> getData(){
        return list;
    }

    /**
     * 根据id删除时间数据
     * @param clockId
     */
    public void deleClockVO(int clockId){
        int index = -1;
        for (int i=0;i<list.size(); i++){
            if(clockId==list.get(i).getClockId()){
                index = i;
                break;
            }
        }
        if(index != -1){
            list.remove(index);
        }
    }

}
